package org.aom.service.base;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import org.springside.modules.orm.Page;
import org.springside.modules.orm.PropertyFilter;
import org.springside.modules.orm.hibernate.HibernateDao;
import org.springside.modules.utils.ReflectionUtils;

/**
 * Service层领域对象业务管理类基类.
 * 使用HibernateDao<T,PK>进行业务对象的CRUD操作,子类需重载getEntityDao()函数提供该DAO.
 * 
 * @param <T>
 *            领域对象类型
 * @param <PK>
 *            领域对象的主键类型
 * 
 *            eg. public class UserManager extends EntityManager<User, Long>{ }
 * 
 * @author calvin
 */
@Transactional
public abstract class EntityManager<T, PK extends Serializable> implements
		IEntityManager<T, PK> {

	protected Logger logger = LoggerFactory.getLogger(getClass());

	/**
	 * 在子类实现此函数,为下面的CRUD操作提供DAO.
	 */
	protected abstract HibernateDao<T, PK> getEntityDao();

	// CRUD函数 //

	@Transactional(readOnly = true)
	public T get(final PK id) {
		return getEntityDao().get(id);
	}

	@Transactional(readOnly = true)
	public Page<T> getAll(final Page<T> page) {
		return getEntityDao().getAll(page);
	}

	@Transactional(readOnly = true)
	public List<T> getAll() {
		return getEntityDao().getAll();
	}

	@Transactional(readOnly = true)
	public List<T> getAllDistinct() {
		return getEntityDao().distinct(getAll());
	}

	@Transactional(readOnly = true)
	public Page<T> search(final Page<T> page, final List<PropertyFilter> filters) {
		return getEntityDao().findPage(page, filters);
	}

	public void save(final T entity) {
		getEntityDao().save(entity);
	}

	public void delete(final PK id) {
		getEntityDao().delete(id);
	}

	public T findByUniqueProperty(String propertyName, Object val) {
		return getEntityDao().findUniqueBy(propertyName, val);
	}

	public List<T> findBy(final String propertyName, final Object value) {
		List<T> result = getEntityDao().findBy(propertyName, value);
		if (result == null || result.size() == 0) {
			return new ArrayList<T>(0);
		} else {
			return result;
		}
	}

	public List<T> findByIds(PK[] ids) {

		if (ids == null || ids.length == 0) {
			return new ArrayList<T>(0);
		} else {
			List<PK> idList = (List<PK>) Arrays.asList(ids);
			return getEntityDao().findByIds(idList);
		}
	}

	/**
	 * 该方法提供给UIDataGrid的getElementsById方法调用
	 * 只能暂时这么委屈一下了，其实不该写在这里的，但是谁让那个主键都是String的呢...
	 * @param idArray
	 * @return
	 * @throws ClassCastException
	 */
	@Transactional(readOnly = true)
	@SuppressWarnings("unchecked")
	public List<T> getElementsById(String[] idArray) throws ClassCastException {
		if(idArray == null||idArray.length==0){
			return new ArrayList<T>(0);
		}else{
			List<PK> ids = new ArrayList<PK>(0);
			for(int i = 0 ; i<idArray.length;i++){
				ids.add((PK)Long.valueOf(idArray[i]));
			}
			return getEntityDao().findByIds(ids);
		}
	}

	public List<T> findByHql(String hql, Object... values) {
		return getEntityDao().find(hql, values);
	}

	@SuppressWarnings("unchecked")
	public List<T> getElements(DetachedCriteria dc, int start, int limit,
			String sortName, boolean direction) {
		if (direction) {
			dc.addOrder(Order.asc(sortName));
		} else {
			dc.addOrder(Order.desc(sortName));
		}
		return (List<T>) getEntityDao().findByCriteria(dc, start, limit).list();
	}
	
	/**
	 * 在没有任何条件限制写获得一个列表
	 */
	public List<T> getElements(int start, int limit, String sortName,
			boolean direction) {
		return getElements(DetachedCriteria.forClass(ReflectionUtils.getSuperClassGenricType(getClass())), start, limit,
				sortName, direction);
	}
	
	public Integer totalCount(){
		return totalCount(DetachedCriteria.forClass(ReflectionUtils.getSuperClassGenricType(getClass())));
	}

	@Transactional(readOnly = true)
	public Integer totalCount(DetachedCriteria dc) {
		return getEntityDao().totalCount(dc);
	}
}
